package com.wk.shiro;

import com.wk.entity.ManagerInfo;
import com.wk.entity.SysRole;
import com.wk.entity.TPermission;
import com.wk.service.impl.ManagerInfoServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private ManagerInfoServiceImpl managerInfoService;


    /*
    * 认证信息（身份验证）
    * @param token
    * @return
    * @throws AuthenticationException
    * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        log.info("MyShiroRealm.doGetAuthenticationInfo()---验证用户身份");
        //获取用户输入的账户
        String username = (String)token.getPrincipal();
        log.info("获取的用户账户为： " + username);
        //通过username从数据库中查找ManagerInfo对象
        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也有时间间隔机制，2分钟内不会重复执行此方法
        ManagerInfo managerInfo = managerInfoService.findByUsername(username);
        if (managerInfo == null)
        {
            return null;
        }
        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配（可自己自定义实现）
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(managerInfo,//用户
                managerInfo.getPassword(), //密码
                ByteSource.Util.bytes(managerInfo.getCredentialsSalt()), //salt = username+salt
                getName() //realm name （这里是指myshirorealm）
        );


        return authenticationInfo;
    }

    /*
    * 此方法调用hasRole,hasPermission的时候才会进行回调.
    * 权限信息（授权）：
    * 1、如果用户正常退出，缓存自动清空
    * 2、如果用户非正常退出，缓存自动清空
    * 3、如果我们修改了用户权限，而用户不退出系统，修改的权限无法立即生效
    * （需要手动编程实现，放在service进行调用）
    * 在权限修改后调用realm中的方法，realm已经由spring管理，所有从spring中获取realm实例，调用clearCache方法；
    * Authorization是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如果访问某个链接，某个资源文件等。
    * @param principals
    * @return
    * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /*
        * 当没有使用缓存的时候，不断刷新页面的话，这个代码会不断执行
        * 当其实没有必要每次都重新设置权限信息，所以我们需要放到缓存中进行管理；
        * 当放到缓存中，这样的话，doGetauthorizationInfo就只会执行一次了
        * 缓存过期后再次执行
        * */
        log.info("权限配置---> MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authenticationInfo =new SimpleAuthorizationInfo();
        ManagerInfo managerInfo = (ManagerInfo)principals.getPrimaryPrincipal();

        //设置相应角色的权限信息
        for(SysRole role : managerInfo.getRoles())
        {
            //设置角色
            authenticationInfo.addRole(role.getRole());
            for (TPermission p : role.getPermissions())
            {
                authenticationInfo.addStringPermission(p.getPermission());
            }
        }
        return authenticationInfo;
    }

    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher)
    {
        HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
        md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.HASH_ALGORITHM_NAME);
        md5CredentialsMatcher.setHashIterations(ShiroKit.HASH_ITERATIONS);
        super.setCredentialsMatcher(md5CredentialsMatcher);
    }


}
